package sip

import (
	"fmt"
	"log"
	"regexp"
	"strings"
)

type Request struct {
	Method    string
	URI       string
	Version   string
	Headers   []string
	HeaderMap map[string]string
	Header    string
	Body      string
}

func UnmarshalRequest(header, body string) (req *Request, err error) {
	lines := strings.Split(strings.TrimSpace(header), "\r\n")
	if len(lines) == 0 {
		err = fmt.Errorf("request with 0 lines header")
		return
	}
	items := regexp.MustCompile("\\s+").Split(strings.TrimSpace(lines[0]), -1)
	if len(items) < 3 {
		err = fmt.Errorf("request uri incorrect,lines:%v", lines)
		return
	}
	headers := make([]string, 0)
	headerMap := make(map[string]string)
	for i := 1; i < len(lines); i++ {
		line := strings.TrimSpace(lines[i])
		headerItems := regexp.MustCompile(":\\s+").Split(line, 2)
		if len(headerItems) < 2 {
			continue
		}
		headers = append(headers, headerItems[0])
		headerMap[headerItems[0]] = headerItems[1]
	}
	req = &Request{
		Method:    items[0],
		URI:       items[1],
		Version:   items[2],
		Headers:   headers,
		HeaderMap: headerMap,
		Header:    header,
		Body:      body,
	}
	return
}

func (req *Request) String() string {
	str := fmt.Sprintf("%s %s %s\r\n", req.Method, req.URI, req.Version)
	for _, key := range req.Headers {
		str += fmt.Sprintf("%s: %s\r\n", key, req.HeaderMap[key])
	}
	str += "\r\n"
	str += req.Body
	return str
}

func (req *Request) SetBody(body string) *Request {
	req.Body = body
	req.SetHeader("Content-Length", fmt.Sprintf("%d", len(body)))
	return req
}

func (req *Request) SetHeader(key, value string) *Request {
	if _, ok := req.HeaderMap[key]; !ok {
		req.Headers = append(req.Headers, key)
	}
	req.HeaderMap[key] = value
	return req
}

func (req *Request) FixVia(t *Transport) *Request {
	reg := regexp.MustCompile(`rport(;|$)`)
	if reg.MatchString(req.HeaderMap["Via"]) {
		newVia := strings.Replace(req.HeaderMap["Via"], "rport", fmt.Sprintf("rport=%d;received=%s", t.RemotePort, t.RemoteIP), 1)
		req.SetHeader("Via", newVia)
	}
	return req
}

func (req *Request) RemoveHeader(key string) *Request {
	delete(req.HeaderMap, key)
	for i := 0; i < len(req.Headers); i++ {
		if req.Headers[i] == key {
			req.Headers = append(req.Headers[:i], req.Headers[i+1:]...)
			i--
		}
	}
	return req
}

func (req *Request) GetContentLength() (length int) {
	length = len(req.Body)
	return
}

func (req *Request) GetDigest() (d *Digest) {
	auth := req.HeaderMap["Authorization"]
	if auth == "" {
		return
	}
	items := regexp.MustCompile("\\s+").Split(auth, 2)
	if len(items) < 2 {
		return
	}
	d = &Digest{}
	d.Entity = req.Body
	d.Scheme = items[0]
	d.Method = req.Method
	switch strings.ToLower(d.Scheme) {
	case "digest":
		rawParams := items[1]
		items = strings.Split(rawParams, ",")
		for i := 0; i < len(items); i++ {
			item := strings.TrimSpace(items[i])
			kv := strings.SplitN(item, "=", 2)
			if len(kv) < 2 {
				continue
			}
			k := strings.TrimSpace(kv[0])
			v := Unquote(strings.TrimSpace(kv[1]))
			switch strings.ToLower(k) {
			case "username":
				d.Username = v
			case "realm":
				d.Realm = v
			case "nonce":
				d.Nonce = v
			case "uri":
				d.URI = v
			case "response":
				d.Response = v
			case "algorithm":
				d.Algorithm = v
			case "cnonce":
				d.CNonce = v
			case "qop":
				d.Qop = v
			case "nc":
				d.NC = v
			}
		}
	case "basic":
		d.Response = items[1]
	}
	return
}

func (req *Request) SendByTransport(transport *Transport) (err error) {
	defer func() {
		if err != nil {
			log.Println(err)
		}
	}()
	buf := []byte(req.String())
	bufLen := len(buf)
	n, e := transport.Write(buf)
	if e != nil {
		err = fmt.Errorf("request send failed, %v", e)
		return
	}
	if n != bufLen {
		err = fmt.Errorf("request send failed, send not complete")
		return
	}
	//Logf("发送：[%s]>>>>>>[%s:%d]\r\n%v", transport.UDPConn.LocalAddr().String(), transport.RemoteIP, transport.RemotePort, req)
	if transport.Protocol=="UDP" {
		Logf("发送：[UDP]：[%s]>>>>>>[%s:%d]\r\n%v", transport.UDPConn.LocalAddr().String(), transport.RemoteIP, transport.RemotePort, req) 
	} else {
		Logf("发送：[TCP]：[%s]>>>>>>[%s:%d]\r\n%v", transport.TCPConn.LocalAddr().String(),transport.RemoteIP, transport.RemotePort, req)
	}
	return
}

func (req *Request) MakeResponse() (res *Response) {
	res = &Response{
		Version:    req.Version,
		StatusCode: 200,
		Status:     "OK",
		Headers:    []string{"Via", "From", "To", "CSeq", "Call-ID", "User-Agent"},
		HeaderMap: map[string]string{
			"Via":     req.HeaderMap["Via"],
			"From":    req.HeaderMap["From"],
			"To":      req.HeaderMap["To"],
			"CSeq":    req.HeaderMap["CSeq"],
			"Call-ID": req.HeaderMap["Call-ID"],
			"User-Agent": "ZTRS SIP Server",
		},
	}
	res.SetBody("")
	res.EnsureToTag()
	return
}

func (req *Request) MakeContactResponse(contact string) (res *Response) {
	res = &Response{
		Version:    req.Version,
		StatusCode: 200,
		Status:     "OK",
		Headers:    []string{"Via", "From", "To", "CSeq", "Call-ID", "User-Agent","Contact"},
		HeaderMap: map[string]string{
			"Via":     req.HeaderMap["Via"],
			"From":    req.HeaderMap["From"],
			"To":      req.HeaderMap["To"],
			"CSeq":    req.HeaderMap["CSeq"],
			"Call-ID": req.HeaderMap["Call-ID"],
			"User-Agent": "ZTRS SIP Server",
			"Contact": contact,
		},
	}
	res.SetBody("")
	res.EnsureToTag()
	return
}
